/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.colswe.solestac.control;

import java.util.ArrayList;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.NoResultException;
import javax.persistence.NonUniqueResultException;
import org.colswe.solestac.VO.StudentVO;
import org.colswe.solestac.dao.ProgramDao;
import org.colswe.solestac.dao.StudentDao;
import org.colswe.solestac.entity.Program;
import org.colswe.solestac.entity.Student;

/**
 *
 * @author Edison Parra
 * @version 1.0
 * @since Version 1.1
 */
public class StudentControl {
    
    /**
     * ALL GOOD = 0.
     */
    public static final int ALL_GOOD = 0;
    /**
     * ERROR DB = -8.
     */
    public static final int ERROR_DB = -8;
    /**
     * ERROR VALIDATION = -11.
     */
    public static final int ERROR_VALIDATION = -11;
    /**
     * INVALID CODE FORMAT = -8.
     */
    public static final int INVALID_CODE_FORMAT = ERROR_DB;
    /**
     * INVALID CODE LENGTH = -9.
     */
    public static final int INVALID_CODE_LENGTH = -9;
    /**
     * INVALID DOCUMENT FORMAT = -1.
     */
    public static final int INVALID_DOCUMENT_FORMAT = -1;
    /**
     * INVALID DOCUMENT LENGTH = -2.
     */
    public static final int INVALID_DOCUMENT_LENGTH = -2;
    /**
     * INVALID LASTNAME FORMAT = -6.
     */
    public static final int INVALID_LASTNAME_FORMAT = -6;
    /**
     * INVALID LASTNAME LENGTH = -7.
     */
    public static final int INVALID_LASTNAME_LENGTH = -7;
    /**
     * INVALID NAME FORMAT = -4.
     */
    public static final int INVALID_NAME_FORMAT = -4;
    /**
     * INVALID NAME LENGTH = -5.
     */
    public static final int INVALID_NAME_LENGTH = -5;
    /**
     * INVALID PROGRAM = -10.
     */
    public static final int INVALID_PROGRAM = -10;
    /**
     * STUDENT ALREADY EXIST = -3.
     */
    public static final int STUDENT_ALREADY_EXIST = -3;
    /**
     * VALID CODE LENGTH = 6.
     */
    public static final int VALID_CODE_LENGTH = 6;

    /**DAO object to manage Program objects.
     */
    private ProgramDao programDao = new ProgramDao();
    /**DAO object to manage Student objects.
     */
    private StudentDao studentDao = new StudentDao();
    /**EntityManagerFactory created to manage the Persistenc Unit.
     */
    private static EntityManagerFactory emf = Persistence.createEntityManagerFactory("SolestacWebPU");
    /**LogControl object to record events in the log.
     */
    private LogControl log;
    
    /**Searches a student by his document.
     * @param document Document
     * @return a StudentVO instance that contains the user data
     */
    public List<StudentVO> searchStudentByDocument(final String document) {
        List<Student> students;
        List<StudentVO> studentVO = null;
        EntityManager em = emf.createEntityManager();
        em.getTransaction().begin();
        try {
            students = studentDao.searchByIdentification(document, em);
            studentVO = convertStudents(students);
        } catch (NoResultException ex) {
            studentVO = new ArrayList<StudentVO>();
        } catch (NonUniqueResultException ex) {
            throw ex;
        } finally {
            em.close();
            return studentVO;
        }
    }

    /**Validate the format and length of the document given.
     * @param document Document
     * @return number of the result
     */
    public int validateDocument(final String document) {
        try {
            if (!verifyDocumentFormat(document)) {
                return INVALID_DOCUMENT_FORMAT;
            }
            if (!verifyDocumentLength(document)) {
                return INVALID_DOCUMENT_LENGTH;
            }
            return 0;
        } catch (Exception ex) {
            log.logging(ex);
            return STUDENT_ALREADY_EXIST;
        }

    }

    /**Creates a student.
     * @param studentVO The Value Object with the new student information.
     * @return A number indicating the result of the operation.
     */
    public int registerStudent(final StudentVO studentVO) {
        int result = verifyInfo(studentVO);
        if (result != 0) {
            return result;
        }
        EntityManager em = emf.createEntityManager();
        Student student = convertVO(studentVO);
        em.getTransaction().begin();
        try {
            
            Program program  = (programDao.searchByName(studentVO.getProgram(), em)).get(0);
            program.getStudentList().add(student);
            programDao.update(program, em);
            
            studentDao.create(student, em);
            em.getTransaction().commit();
            return 0;
        } catch (NoResultException ex) {
            log.logging(ex);
            return ERROR_DB;
        } catch (NonUniqueResultException ex) {
            log.logging(ex);
            return ERROR_DB;
        } finally {
            em.close();
        }
    }
    
    /**Verifies the info of the student.
     * @param student The object with the information of the student.
     * @return A number indicating the result of the operation.
     */
    public int verifyInfo(final StudentVO student) {
        try {
            if (!verifyDocumentFormat(student.getDocument())) {
                return INVALID_DOCUMENT_FORMAT;
            }
            if (!verifyDocumentLength(student.getDocument())) {
                return INVALID_DOCUMENT_LENGTH;
            }
            if (existsStudent(student.getDocument())) {
                return STUDENT_ALREADY_EXIST;
            }
            if (!verifyNamesFormat(student.getName())) {
                return INVALID_NAME_FORMAT;
            }
            if (!verifyNamesLength(student.getName())) {
                return INVALID_NAME_LENGTH;
            }
            if (!verifyNamesFormat(student.getLastName())) {
                return INVALID_LASTNAME_FORMAT;
            }
            if (!verifyNamesLength(student.getLastName())) {
                return INVALID_LASTNAME_LENGTH;
            }
            if (!verifyCodeFormat(student.getCode())) {
                return INVALID_CODE_FORMAT;
            }
            if (!verifyCodeLength(student.getCode())) {
                return INVALID_CODE_LENGTH;
            }
            if (!verifyValue(student.getProgram())) {
                return INVALID_PROGRAM;
            }
            return ALL_GOOD;
        } catch (Exception ex) {
            log.logging(ex);
            return ERROR_VALIDATION;
        }
    }

    /**Create a List<Students_VO> from a List<Student> using the private method convert in a for-each loop.
     * @param students a List of Student
     * @return a List of StudentVO
     */
    private List<StudentVO> convertStudents(final List<Student> students) {
        List<StudentVO> studentsVO = new ArrayList<StudentVO>();
        for (Student x : students) {
            StudentVO studentVO = convert(x);
            studentsVO.add(studentVO);
        }
        return studentsVO;
    }

    /**Creates a StudentVO from a Student data.
     * @param student a Student instance
     * @return a StudentVO
     */
    private StudentVO convert(final Student student) {
        StudentVO studentVO = new StudentVO();
        studentVO.setId(student.getId());
        studentVO.setName(student.getName());
        studentVO.setLastName(student.getLastName());
        studentVO.setDocument(student.getDocument());
        studentVO.setProgram(student.getProgramId().getName());
        studentVO.setCode(student.getCode());
        return studentVO;
    }
    
    /**Verifies the format of the document given.
     * @param document The documents string.
     * @return true if the document format is correct. false otherwise
     */
    public boolean verifyDocumentFormat(final String document) {
        return (document.matches("^[0-9]*$"));
    }

    /**Verifies the length of the document given.
     * @param document The document string
     * @return true if the document length is correct. false otherwise
     */
    public boolean verifyDocumentLength(final String document) {
        return (document.matches(".{6,12}"));
    }
    
    /**Verifies if already exists a student with the same document.
     * @param document The Document string
     * @return true if the document exists in the data base. False otherwise
     */
    public boolean existsStudent(final String document) {
        EntityManager em = emf.createEntityManager();
        em.getTransaction().begin();
        try {
            List<Student> student = studentDao.searchByIdentification(document, em);
            return (!student.isEmpty());
        } catch (NoResultException ex) {
            return false;
        } catch (NonUniqueResultException ex) {
            log.logging(ex);
            return false;
        } finally {
            em.close();            
        }
    }
    
    /**Verifies the format of the name given.
     * @param name The name string.
     * @return true if the name format is correct. false otherwise
     */
    private boolean verifyNamesFormat(final String name) {
        return (name.matches("^[A-ZÑ][a-zñ]+\\s[A-ZÑ][a-zn]+$"));
    }

    /**Verifies the length of the name given.
     * @param name The name string.
     * @return true if the name length is correct. false otherwise
     */
    public boolean verifyNamesLength(final String name) {
        return (name.matches(".{2,45}"));
    }
    
    /**Verifies the format of the code given.
     * @param code The code string
     * @return true if the code format is correct. false otherwise
     */
    private boolean verifyCodeFormat(final String code) {
        return (code.matches("^[0-9]+$"));
    }
    
    /**Verifies the length of the code given.
     * @param code The code string.
     * @return true if the code length is correct. false otherwise
     */
    public boolean verifyCodeLength(final String code) {
        return (code.length() == VALID_CODE_LENGTH);
    }
    
    /**Verifies that the value of the list be different of "Seleccione uno...".
     * @param listValue The string with the selected value in the list.
     * @return true if the listValue is diferent from "Seleccione Uno". false otherwise
     */
    public boolean verifyValue(final String listValue) {
        return (!listValue.equals("Seleccione uno..."));
    }
    
    /**Convert a StudentVO objecto into a Student object.
     * @param studentVO The objecto to convert.
     * @return The Student object.
     */
    private Student convertVO(final StudentVO studentVO) {

        EntityManager em = emf.createEntityManager();
        em.getTransaction().begin();
        Student student = null;
        try {
            student = new Student();
            student.setName(studentVO.getName());
            student.setLastName(studentVO.getLastName());
            student.setDocument(studentVO.getDocument());
            student.setCode(studentVO.getCode());
            
            Program program = (programDao.searchByName(studentVO.getProgram(), em)).get(0);
            student.setProgramId(program);
            
        } catch (Exception ex) {
            throw ex;
        } finally {
            em.close();
            return student;
        }
    }
    
    /**Gets the list of all programs in the system.
     * @return The list of program names.
     */
    public List<String> getAllPrograms() {
        List<String> programs = null;
        EntityManager em = emf.createEntityManager();
        try {
            List<Program> programList = programDao.searchAll(em);
            programs = convertPrograms(programList);
        } catch (Exception ex) {
            log.logging(ex);
        } finally {
            em.close();
            return programs;
        }
    }
    
    /**Converts a list of Program objects into a list of String objects.
     * @param programList The list of Program objects.
     * @return The list of String objects.
     */
    private List<String> convertPrograms(final List<Program> programList) {
        List<String> programs = new ArrayList<String>();
        for (Program program : programList) {
            String stringProgram = program.getName();
            programs.add(stringProgram);
        }
        return programs;
    }
    
}
